﻿using System.Linq;
using System.Linq.Expressions;
using Apex.DomainCommons.Models;
using Dapper;

namespace Microsoft.EntityFrameworkCore
{
    /// <summary>
    /// EF Core扩展类
    /// </summary>
    public static class EFCoreExtensions
    {
        /// <summary>
        /// set global 'IsDeleted=false' queryfilter for every entity
        /// 简化软删除功能的实现：每个实体启用软删除的全局查询过滤器（Global Query Filter）
        /// </summary>
        /// <param name="modelBuilder"></param>
        public static void EnableSoftDeletionGlobalFilter(this ModelBuilder modelBuilder)
        {
            // 通过 modelBuilder.Model.GetEntityTypes() 获取模型中的所有实体类型
            var entityTypesHasSoftDeletion = modelBuilder.Model.GetEntityTypes()
            // 通过 LINQ 查询找到所有实现了 ISoftDelete 接口的实体类型
                .Where(e => e.ClrType.IsAssignableTo(typeof(ISoftDelete)));

            foreach (var entityType in entityTypesHasSoftDeletion)
            {
                // 对于每个具有软删除功能的实体类型，获取对应的 IsDeleted 属性
                var isDeletedProperty = entityType.FindProperty(nameof(ISoftDelete.IsDeleted));
                // 并构建一个表达式树来表示查询过滤条件，即 IsDeleted=false
                var parameter = Expression.Parameter(entityType.ClrType, "p");
                var filter = Expression.Lambda(Expression.Not(Expression.Property(parameter, isDeletedProperty.PropertyInfo)), parameter);
                // 通过 entityType.SetQueryFilter(filter) 将构建好的查询过滤器应用到实体类型上，以便在查询时自动过滤掉已被软删除的记录
                entityType.SetQueryFilter(filter);
            }
        }

        // 批量增删改参考：https://github.com/yangzhongke/Zack.EFCore.Batch/blob/main/README_CN.md

        /// <summary>
        /// 为 DbContext 类型添加一个通用的查询方法，提高查询性能
        /// </summary>
        /// <typeparam name="T">接受一个泛型类型参数 T，要求 T 必须是一个类并且实现了 IEntity 接口</typeparam>
        /// <param name="ctx"></param>
        /// <returns>数据集</returns>
        public static IQueryable<T> Query<T>(this DbContext ctx) where T : class, IEntity
        {
            // 使用 ctx.Set<T>().AsNoTracking() 获取指定实体类型的查询集
            // 并通过 AsNoTracking() 方法告诉 Entity Framework Core 不要跟踪返回的实体对象，以提高查询性能并避免实体对象被上下文管理
            return ctx.Set<T>().AsNoTracking();
        }

        /// <summary>
        /// 原生SQL查询实体集
        /// </summary>
        /// <typeparam name="TEntity">返回的实体类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <returns>实体集合</returns>
        public static async Task<IEnumerable<TEntity>> QueryBySQLAsync<TEntity>(this DbContext ctx,string sql) where TEntity : class, IEntity
        {
            var res = await ctx.Database.GetDbConnection().QueryAsync<TEntity>(sql);
            return res;
        }

    }
}
